#!/usr/bin/env lua

function table.isArray(tab)
    if not tab then
        return false
    end

    local ret = false
    local idx = 1
    for f, v in pairs(tab) do
        if type(f) ~= "number" or f ~= idx then
            return false
        end

        idx = idx + 1
    end

    return true
end

function serialize(t)
    local mark = {}
    local assign = {}

    local function table2str(t, parent)
        mark[t] = parent
        local ret = {}

        if table.isArray(t) then
            table.foreach(t, function(i, v)
                local k = tostring(i)
                local dotkey = parent .. "[" .. k .. "]"
                local t = type(v)
                if t == "userdata" or t == "function" or t == "thread" or t == "proto" or t == "upval" then

                elseif t == "table" then
                    if mark[v] then
                        table.insert(assign, dotkey .. "=" .. mark[v])
                    else
                        table.insert(ret, table2str(v, dotkey))
                    end
                elseif t == "string" then
                    table.insert(ret, string.format("%q", v))
                elseif t == "number" then
                    if v == math.huge then
                        table.insert(ret, "math.huge")
                    elseif v == -math.huge then
                        table.insert(ret, "-math.huge")
                    else
                        table.insert(ret, tostring(v))
                    end
                else
                    table.insert(ret, tostring(v))
                end
            end)
        else
            table.foreach(t, function(f, v)
                local k = type(f) == "number" and "[" .. f .. "]" or f
                local dotkey = parent .. (type(f) == "number" and k or "." .. k)
                local t = type(v)
                if t == "userdata" or t == "function" or t == "thread" or t == "proto" or t == "upval" then
                    --ignore
                elseif t == "table" then
                    if mark[v] then
                        table.insert(assign, dotkey .. "=" .. mark[v])
                    else
                        table.insert(ret, string.format("%s=%s", k, table2str(v, dotkey)))
                    end
                elseif t == "string" then
                    table.insert(ret, string.format("%s=%q", k, v))
                elseif t == "number" then
                    if v == math.huge then
                        table.insert(ret, string.format("%s=%s", k,  "math.huge"))
                    elseif v == -math.huge then
                        table.insert(ret, string.format("%s=%s", k,  "-math.huge"))
                    else
                        table.insert(ret, string.format("%s=%s", k, tostring(v)))
                    end
                else
                    table.insert(ret, string.format("%s=%s", k, tostring(v)))
                end
            end)
        end

        return "{" .. table.concat(ret, ",") .. "}"
    end

    if type(t) == "table" then
        return string.format("%s%s", table2str(t, "-"), table.concat(assign, " "))
    else
        return tostring(t)
    end
end

function unserialize(str)
    if str == nil or str == "nil" then
        return nil
    elseif type(str) ~= "string" or #str == 0 then
        return {}
    end

    local code, ret = pcall(loadstring(string.format("do local _=%s return _ end", str)))
    if not code then
        return {}
    end

    return ret
end

local a = {1,2,3,4,5,6}
a["st"] = "st"
a["t"] = {["m"] = "xx", b=1}
print(serialize(a))
local after = unserialize((serialize(a)))
print(after["t"].m)
